/*
 * Copyright 2008 Andrej Herich
 * 
 * http://azetclient.yw.sk/
 * http://azetclient.googlecode.com/
 * 
 * This file is part of AzetClient.
 * 
 * AzetClient is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * AzetClient is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with AzetClient.  If not, see <http://www.gnu.org/licenses/>.
 */

package sk.yw.azetclient.azet;

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.log4j.Logger;
import sk.yw.azetclient.Main;
import sk.yw.azetclient.model.Buddy;

/**
 *
 * @author error216
 */
public class AzetMessageSplitterAndCondenser {
    
    public static final Logger logger = Logger.getLogger(AzetMessageSplitterAndCondenser.class);
    
    public enum MessageKey {
        A, B, C, D, E, F, G, H, I, J,
        K, L, M, N, O, P, Q, R, S, T,
        U, V, W, X, Y, Z;
        
        public MessageKey next() {
            return values()[(ordinal() + 1) % values().length];
        }
    }
    
    private static final MessageKey FIRST_KEY = MessageKey.A;
    
    private class MessageKeyStorage {
        private Map<Buddy,MessageKey> lastUsedKeys = new HashMap<Buddy,MessageKey>();
        
        public MessageKey getNextKey(Buddy buddy) {
            if (buddy == null)
                throw new IllegalArgumentException("Null pointer in buddy");
            
            if (!lastUsedKeys.containsKey(buddy)) {
                lastUsedKeys.put(buddy, FIRST_KEY);
                logger.debug("Returning first key: " + FIRST_KEY);
                return FIRST_KEY;
            }
            
            lastUsedKeys.put(buddy, lastUsedKeys.get(buddy).next());
            logger.debug("Returning key: " + lastUsedKeys.get(buddy));
            return lastUsedKeys.get(buddy);
        }
    }
    
    private static int maxLength;
    
    private static final Pattern CONDENSE_PATTERN =
            Pattern.compile("\\[([A-Z])([1-9])/([2-9])\\] (.*)");
    private static final String SPLIT_PATTERN = "[A1/9] M";
    private static final int SPLIT_PATTERN_LENGTH = 7;
    
    private Map<MessageKey,Map<Buddy,MessageCondenser>> builders =
            new EnumMap<MessageKey,Map<Buddy,MessageCondenser>>(MessageKey.class);
    private MessageKeyStorage keys = new MessageKeyStorage();
    
    private static void refreshProperties() {
        maxLength = 245;
        try {
            maxLength = Integer.parseInt(
                Main.getProperty("azetclient.azet.messages.maxLength"));
        } catch (NumberFormatException ex) {
            logger.error("Could not parse max length of message from properties file: "
                    + Main.getProperty("azetclient.azet.messages.maxLength"), ex);
        }
    }
    
    public List<String> split(AzetMessage message) {
        if (message == null)
            throw new IllegalArgumentException("Null pointer in message");
        if (message.getContent() == null)
            throw new IllegalArgumentException("Null pointer in message content");
        
        refreshProperties();
        int splitIndex = maxLength - SPLIT_PATTERN_LENGTH;
        
        ArrayList<String> parts = new ArrayList<String>();
        StringBuilder content = new StringBuilder(message.getContent());
        
        // split message to parts
        while (content.length() > maxLength) {
            int i = content.lastIndexOf(" ", splitIndex);
            if (i == -1) {
                parts.add(content.substring(0, splitIndex));
                content.delete(0, splitIndex);
            } else {
                parts.add(content.substring(0, i));
                content.delete(0, i);
            }
        }
        parts.add(content.toString());
        
        // apply split pattern to messages
        // which means basically to prepend [A1/9] thing to message
        if (parts.size() > 1) {
            MessageKey key = keys.getNextKey(message.getReceiver()); 
            // remove parts if there are more than 9
            while (parts.size() > 9) {
                parts.remove(parts.size() - 1);
            }
            for (int i = 1; i <= parts.size(); i++) {
                String part = SPLIT_PATTERN
                        .replace("A", key.toString())
                        .replace("1", String.valueOf(i))
                        .replace("9", String.valueOf(parts.size()))
                        .replace("M", parts.get(i - 1));
                parts.set(i - 1, part);
            }
        }
        
        return parts;
    }
    
    public List<AzetMessageBean> condense(List<AzetMessageBean> messages) {
        if (messages == null)
            throw new IllegalArgumentException("Null pointer in messages");
        
        refreshProperties();
        List<AzetMessageBean> condensedMessages =
                new ArrayList<AzetMessageBean>();
        
        for (AzetMessageBean message : messages) {
            Matcher matcher = CONDENSE_PATTERN.matcher(message.getContent());
            if (matcher.matches()) {
                MessageKey key = null;
                try {
                    key = MessageKey.valueOf(matcher.group(1));
                } catch (IllegalArgumentException ex) {
                    logger.error("Illegal message key: " + message.getContent());
                    condensedMessages.add(message);
                }
                
                MessageCondenser builder = null;
                if (!builders.containsKey(key)) {
                    builder = new MessageCondenser(matcher, message);
                    Map<Buddy,MessageCondenser> map =
                            new HashMap<Buddy,MessageCondenser>();
                    map.put(message.getSender(), builder);
                    builders.put(key, map);
                    
                } else if (!builders.get(key)
                        .containsKey(message.getSender())) {
                    builder = new MessageCondenser(matcher, message);
                    builders.get(key).put(message.getSender(), builder);
                    
                } else {
                    builder = builders.get(key).get(message.getSender());
                    builder.add(matcher, message);
                }
                
                if (builder.isComplete()) {
                    builders.remove(key);
                }
                
                condensedMessages.add(builder.getCondensedMessage());
            } else {
                condensedMessages.add(message);
            }
        }
        
        return condensedMessages;
    }
}
